package com.example.codediff.analyzer;

import org.objectweb.asm.*;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
import org.springframework.stereotype.Component;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 类分析器
 * 使用ASM框架分析class文件的结构和方法指令
 */
@Component
public class ClassAnalyzer extends ClassVisitor {
    // 类名
    private String className;
    // 存储方法信息的列表
    private final List<MethodInfo> methods = new ArrayList<>();
    // 存储常量信息
    private final List<ConstantInfo> constants = new ArrayList<>();
    // 用于收集字节码指令的工具
    private final StringWriter sw = new StringWriter();
    private final PrintWriter pw = new PrintWriter(sw);
    private final Printer printer = new Textifier();
    private final TraceMethodVisitor mv = new TraceMethodVisitor(printer);

    /**
     * 构造函数
     * 使用ASM9版本
     */
    public ClassAnalyzer() {
        super(Opcodes.ASM9);
    }

    /**
     * 访问类信息
     * @param version 类版本
     * @param access 访问标志
     * @param name 类名
     * @param signature 泛型签名
     * @param superName 父类名
     * @param interfaces 接口列表
     */
    @Override
    public void visit(int version, int access, String name, String signature,
                     String superName, String[] interfaces) {
        this.className = name.replace('/', '.');
    }

    /**
     * 访问类的方法
     * @param access 访问标志
     * @param name 方法名
     * @param descriptor 方法描述符
     * @param signature 泛型签名
     * @param exceptions 异常列表
     * @return 方法访问器
     */
    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor,
                                   String signature, String[] exceptions) {
        MethodInfo methodInfo = new MethodInfo(name, descriptor);
        methods.add(methodInfo);
        
        return new MethodVisitor(Opcodes.ASM9, mv) {
            @Override
            public void visitEnd() {
                printer.print(pw);
                methodInfo.setInstructions(sw.toString());
                sw.getBuffer().setLength(0); // 清除缓冲区
                super.visitEnd();
            }
        };
    }

    /**
     * 访问类的字段
     */
    @Override
    public FieldVisitor visitField(int access, String name, String descriptor,
                                 String signature, Object value) {
        // 只关注静态常量
        if ((access & Opcodes.ACC_STATIC) != 0 && (access & Opcodes.ACC_FINAL) != 0) {
            constants.add(new ConstantInfo(name, descriptor, value));
        }
        return super.visitField(access, name, descriptor, signature, value);
    }

    /**
     * 获取类名
     * @return 完全限定的类名
     */
    public String getClassName() {
        return className;
    }

    /**
     * 获取方法列表
     * @return 类中的所有方法信息
     */
    public List<MethodInfo> getMethods() {
        return methods;
    }

    /**
     * 获取常量列表
     */
    public List<ConstantInfo> getConstants() {
        return constants;
    }

    /**
     * 方法信息类
     * 存储方法的名称、描述符和字节码指令
     */
    public static class MethodInfo {
        // 方法名
        private final String name;
        // 方法描述符
        private final String descriptor;
        // 字节码指令
        private String instructions;

        /**
         * 构造方法信息对象
         * @param name 方法名
         * @param descriptor 方法描述符
         */
        public MethodInfo(String name, String descriptor) {
            this.name = name;
            this.descriptor = descriptor;
        }

        public String getName() {
            return name;
        }

        public String getDescriptor() {
            return descriptor;
        }

        public String getInstructions() {
            return instructions;
        }

        public void setInstructions(String instructions) {
            this.instructions = instructions;
        }

        /**
         * 比较两个方法是否相同
         * 比较方法名、描述符和字节码指令
         */
        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false;
            MethodInfo that = (MethodInfo) obj;
            return name.equals(that.name) &&
                   descriptor.equals(that.descriptor) &&
                   instructions.equals(that.instructions);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name, descriptor, instructions);
        }
    }

    /**
     * 静态常量信息类
     */
    public static class ConstantInfo {
        private final String name;
        private final String descriptor;
        private final Object value;

        public ConstantInfo(String name, String descriptor, Object value) {
            this.name = name;
            this.descriptor = descriptor;
            this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false;
            ConstantInfo that = (ConstantInfo) obj;
            return name.equals(that.name) &&
                   descriptor.equals(that.descriptor) &&
                   Objects.equals(value, that.value);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name, descriptor, value);
        }
    }
}
